/*
 * Space.java
 *
 * Created on 20 February 2004, 15:33
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs.physics;

import us.desertlabs.number.NumberRange;

/**
 * Used to define the multi-dimensional space in which <CODE>PhysicsObject</CODE>s
 * can exist. Each dimension is represented by a single <CODE>NumberRange</CODE>.
 * A particular <CODE>Space</CODE> also has other important attributes, such
 * as various "reality constants" like the gravitational constant.
 * <P>
 * Every <CODE>Space</CODE> acts like a container for the <CODE>PhysicalObject</CODE>s
 * that exist within it. As time passes in <CODE>Space</CODE>, the same
 * amount of time passes upon each of is contained <CODE>PhysicalObject</CODE>s.
 * Various calculations are performed as a result of the passage of time, such
 * as the application of gravitational forces and movement as a result of
 * velocity.
 *
 * @author Brandon Franklin
 */
public class Space {

    /**
     * The <CODE>NumberRange</CODE> instances that define the dimensional
     * shape of space
     */
    final private NumberRange[] ranges;

    /** The gravitational constant to be applied in this <CODE>Space</CODE> */
    private Number gravConstant;

    /**
     * Creates a new instance of <CODE>Space</CODE> with the specified
     * dimensional ranges. The number of dimensions in space will be based on
     * the number of ranges supplied.
     *
     * @param ranges the dimensional ranges in this space
     * @throws IllegalArgumentException if any elements of the array are null,
     *         or if the parameter itself is null or empty
     */
    public Space( final NumberRange[] ranges ) {

        // Validate the parameter itself
        if( (ranges == null) || (ranges.length == 0) ) {
            throw new IllegalArgumentException( "Array of ranges cannot be empty or null." );
        }

        // Validate each range
        for( final NumberRange range : ranges ) {
            if( range == null ) {
                throw new IllegalArgumentException( "Array of ranges cannot contain any null elements." );
            }
        }

        this.ranges = ranges;
        gravConstant = new Integer( 1 );
    }

    /**
     * Returns the array of <CODE>NumberRange</CODE>s that were defined when
     * the <CODE>Space</CODE> was instantiated.
     *
     * @return the array of <CODE>NumberRange</CODE>s that were defined when
     *         the <CODE>Space</CODE> was instantiated
     */
    public NumberRange[] getRanges() {
        return ranges;
    }

    /**
     * Returns the <CODE>NumberRange</CODE> that describes the possible values
     * of the specified dimension in this <CODE>Space</CODE>.
     *
     * @param dimension the dimension in space to return the range for
     * @return the <CODE>NumberRange</CODE> that describes the possible values
     *         of the specified dimension
     * @throws IllegalArgumentException if the supplied dimension is not valid
     */
    public NumberRange getRange( final int dimension ) {

        if( (dimension < 0) || (dimension >= ranges.length) ) {
            throw new IllegalArgumentException( "Dimension requested is out of range." );
        }

        return ranges[dimension];
    }

    /**
     * Returns the gravitational constant used within this <CODE>Space</CODE>.
     *
     * @return the gravitational constant used within this <CODE>Space</CODE>
     */
    public Number getGravitationalConstant() {
        return gravConstant;
    }

    /**
     * Sets the gravitational constant to be used within this <CODE>Space</CODE>.
     *
     * @param gravConstant the gravitational constant to be used within this
     *        <CODE>Space</CODE>
     */
    public void setGravitationalConstant( final Number gravConstant ) {
        this.gravConstant = gravConstant;
    }

    /**
     * Returns the number of dimensions in this space. This is the number of
     * <CODE>NumberRange</CODE>s that were defined when the <CODE>Space</CODE>
     * was instantiated.
     *
     * @return the number of dimensions in this space
     */
    public int getDimensionality() {
        return ranges.length;
    }

    /**
     * Given a set of coordinates, returns true if the coordinates are valid for
     * the ranges defined in this space, false otherwise.
     *
     * @param coords the coordinates to check for conformance to this space
     * @return true if the coordinates are valid for the ranges defined in this
     *         space, false otherwise
     */
    public boolean checkCoordinates( final Number[] coords ) {

        // Confirm dimensionality is the same
        if( getDimensionality() != coords.length ) {
            return false;
        }

        // Check every value
        for( int i = 0; i < ranges.length; i++ ) {
            if( !ranges[i].includes( coords[i] ) ) {
                return false;
            }
        }

        return true;
    }
}
